#include "LuaDBUtils.h"
//LuaDBUtils g_oLuaDBUtils ;

int LuaDBUtils::CheckArray(lua_State* pL,int nStackPos)
{
    int nObjLen = 0;
    lua_pushnil(pL);
    int nMaxIndex = -1;
    int nIndex = 0;
    while(lua_next(pL, nStackPos))
    {
        ++nObjLen;
        if(lua_type(pL,-2) != LUA_TNUMBER)
        { 
            lua_pop(pL, 2);
            return 0;
        } 
        nIndex = lua_tointeger(pL, -2);
        if( nIndex > nMaxIndex)
        { 
            nMaxIndex = nIndex;
        } 
        if( nMaxIndex > nObjLen || nIndex<= 0)
        { 
            lua_pop(pL,2);
            return 0;
        } 
        lua_pop(pL,1);
    } 
    if( nObjLen == nMaxIndex )
    { 
        return nMaxIndex;
    } 
    return 0;
}

bool LuaDBUtils::IsDigit(const char* str)
{
    for(int i=0;str[i];i++)
    {
        if(str[i]<'0' || str[i]>'9')
        {
            return false;
        }
    }
    return true;
}

void LuaDBUtils::SetValue(lua_State* pL,const char* pKey,bool isArray)
{
    if(IsDigit(pKey))
    {
        lua_rawseti(pL, -2, atoi(pKey) + isArray); 
    }
    else
    {
        lua_setfield(pL,-2,pKey); 
    }
}

bool LuaDBUtils::LuaToBson(lua_State* pL,int nStackPos,bson* b,bool isArray)
{
    if( !lua_istable(pL, nStackPos))   
    {
        luaL_error(pL, "LuaToBson must be a table");
        return false;
    }   
    lua_pushnil(pL);
    char szKey[32]={0};
    while(lua_next(pL,nStackPos))
    {
        //处理key
        const char* pKey = NULL;
        if( lua_type(pL, -2) == LUA_TSTRING)
        {
            pKey = lua_tostring(pL, -2);
        } 
        else if( lua_type(pL, -2) == LUA_TNUMBER)
        {
            snprintf(szKey, 31,"%d", (int)lua_tointeger(pL, -2) - isArray);
            pKey = szKey;
        } 
        else 
        {
            luaL_error(pL, " table key require string or number");
            return false;
        }
        
        //---处理value
        switch(lua_type(pL,-1))
        {
            case LUA_TSTRING:
            {
                if(strcmp(pKey,"_id")==0)
                {
                    bson_oid_t oid;
                    bson_oid_from_string( &oid, lua_tostring(pL,-1));
                    bson_append_oid( b, "_id", &oid );
                }
                else
                {
                    bson_append_string(b,pKey,lua_tostring(pL,-1));
                }
            }
            break;

            case LUA_TBOOLEAN:
            {
                bson_append_bool(b,pKey,lua_toboolean(pL,-1));
            }
            break;

            case LUA_TNUMBER:
            {
                bson_append_double(b,pKey,lua_tonumber(pL,-1));
            }
            break;

            case LUA_TTABLE:
            {

                int nArray = CheckArray(pL,lua_gettop(pL));
                if(nArray > 0)
                {
                    bson_append_start_array(b,pKey);
                    if(LuaToBson(pL,lua_gettop(pL),b,true))
                    {
                        bson_append_finish_array(b);
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    bson_append_start_object(b,pKey);
                    if(LuaToBson(pL,lua_gettop(pL),b))
                    {
                        bson_append_finish_object(b);
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            break;

            default:
            {
                luaL_error(pL, "LuaToBson type_key=%d", lua_type(pL,-1));
            }
            break;
        }




        lua_pop(pL,1);   //弹出value,剩下key取下一个key-value 
    }
    return true; 
}

bool LuaDBUtils::BsonToLua(lua_State* pL,const bson* b,bool isArray)
{
    if(!lua_istable(pL, -1)) 
    {
        printf("=========BsonToLua no table=============\n");
        lua_pop(pL, 1);

        lua_newtable(pL);
    }

    bson_iterator it[1];
    bson_iterator_init( it, (bson *)b );
    while( bson_iterator_next( it ) ) 
    {
        const char* pKey = bson_iterator_key( it );
        bool nextIsArray = false;

        switch(bson_iterator_type(it))
        {
            case BSON_DOUBLE:
            case BSON_INT:
            case BSON_LONG:
            {
                lua_pushnumber(pL,bson_iterator_double(it));
            }
            break;
            case BSON_STRING:
            {
                lua_pushstring(pL,bson_iterator_string(it));
            }
            break;
            case BSON_ARRAY:
                nextIsArray = true;
            case BSON_OBJECT:
            {
                if(IsDigit(pKey))
                {
                    lua_rawgeti(pL, -1,atoi(pKey) + isArray);
                    if(!lua_istable(pL, -1))
                    {              
                        lua_pop(pL,1);
                        lua_newtable(pL);
                    }
                }
                else
                {
                    lua_getfield(pL,-1,pKey);
                    if(!lua_istable(pL,-1))
                    {
                        lua_pop(pL,1);
                        lua_newtable(pL);
                    }
                }
                bson sub[1];
                bson_iterator_subobject( it, sub );
                BsonToLua(pL,sub,nextIsArray);
            }
            break;
            case BSON_OID:
            {
                bson_oid_t *oid = bson_iterator_oid(it);
                char _id[128]= {0};
                bson_oid_to_string(oid,_id);
                lua_pushstring(pL,_id);
            }
            break;
            case BSON_BOOL:
            {
                lua_pushboolean(pL,bson_iterator_bool(it));
            }
            break;
            default:
            {
                lua_pushstring(pL,"Bson To Lua ... error bson type");
            }
            break;
        }
        SetValue(pL,pKey,isArray);

    }


    return true;
}


